﻿using System;
using System.Collections.Generic;
using System.Web.UI.WebControls;
using System.Web.UI;
using Grishko.DataTier;
using OutdoorLib.BaseEntity.Infrastructure;
using OutdoorLib.BaseEntity.Filters;
using OutdoorLib.BaseEntity.Contract;
using Ecopsy.Tools;

namespace Grishko.UI
{
    /// <summary>
    /// Контрол отображающий сетку загрузки. Одной стороны.
    /// </summary>
    public class SideSheduleRow: Control
    {
        static string Css_SelCell           = "SelCell";
        static string Css_Vacant_Cell       = "Vacant_Cell";
        static string Css_Prototype_Cell    = "Prototype_Cell";
        static string Css_Contract_Cell     = "Contract_Cell";
        static string Css_Reserv_Cell       = "Reserv_Cell";
        static string Css_SocialAdvice_Cell = "SocialAdvice_Cell";
        static string Css_MySelfAdvice_Cell = "MySelfAdvice_Cell";
        static string Css_Other_Cell        = "Other_Cell";

        readonly Unit Height = new Unit("20px");
        readonly Unit Q_Height = new Unit("3px");
        readonly Unit BoxCellWidtch = new Unit("200px");
        
        readonly string PageMethodFormatKey = "PageMethodFormat";
        

        private class PageMethodParam
        {
            
           public string SideID{get;set;}
           public DateTime dtStart { get; set; }
           public DateTime dtEnd { get; set; }
           public object[] ImAsArray { get { object[] result = { dtStart.ToShortDateString (),
                                                                   dtEnd.ToShortDateString (), 
                                                                  SideID }; 
                                                return result; } }
         
        }

        /// <summary>
        /// Расписание стороны редактировать?
        /// </summary>
        public bool EditMode
        {

            get{   return null!= _SideSheduleParams .CurrentContract;} 
        }

        /// <summary>
        /// Формат команды PageMethod 
        /// </summary>
        public string PageMethodFormat
        {

            get
            {
                object text = ViewState[PageMethodFormatKey];
                if (null == text)
                    return string.Empty;
                else
                    return (string)text;
            }
            set
            {
                ViewState[PageMethodFormatKey] = value;
            }
        }



        #region Хранилище стэка переходов поверхности из состояние в состояние


       

        /// <summary>
        /// Конвертирует ид состояния в соотв. CSS
        /// </summary>
        protected static string Convert_StateIdToCss(string SurfaceStateID)
        {
            switch (SurfaceStateID )            
            {
                case TContractStatus.Reserv:
                    return Css_Reserv_Cell ;

                case TContractStatus.Contract:
                    return Css_Contract_Cell ;

                case TContractStatus.Prototype :
                    return Css_Prototype_Cell;

                case TContractStatus.MySelfAdvice :
                    return Css_MySelfAdvice_Cell;

                case TContractStatus.SocialAdvice :
                    return Css_SocialAdvice_Cell ;

                case TContractStatus.Other:
                    return Css_Other_Cell;
            }
            return Css_Vacant_Cell;

        }
        public class PeriodItem
        {
            public DateTime FirstDay = DateTime.Now;
            public virtual DateTime LastDay {get;set;}

        }

        /// <summary>
        /// Период в опр. состоянии
        /// Клетка в расписании
        /// </summary>
        public class StackItem : PeriodItem
        {

            public string ContractState { get; set; }
            public int DayCount { get; set; }
            public string Hint = string.Empty;
            public bool IsProcessedContractItemsCell = false;
            public int SurfaceNumber { get; set; }
            public override DateTime  LastDay{
    	        get 
	        { 
		        return FirstDay .AddDays (DayCount-1);
	        }
	        set 
	        { 
		        ;
	        }
} 

        }

        #endregion 
        
        /// <summary>
        /// Вернуть заголовок
        /// </summary>
        /// <returns></returns>
        public  Table  GetHeaderRow()
        {
            Table result = new Table() { Width = new Unit("100%") };

            Int16 CellSize = 100;
            CellSize = Convert.ToInt16(100 / (Diff == 0 ? 1 : Diff));
            TableRow rw = new TableRow();
            //Формируем ячейки строчки 
            foreach (PeriodItem p in PeriodList)
            {
                TableCell Cell = new TableCell();
                switch (_SideSheduleParams.Period  )
                {
                    case DateTimeTools.TimePeriod.Day:
                        Cell.Text = p.FirstDay.Day.ToString();
                        if (p.FirstDay.Day < 10)
                            Cell.Text = Cell.Text + "&nbsp&nbsp";
                                                
                        break;
                    case DateTimeTools.TimePeriod.Month:
                        Cell.Text = p.FirstDay.ToString("MMM");
                        
                        break;
                    case DateTimeTools.TimePeriod.Week:
                        Cell.Text = p.FirstDay.Day.ToString() + "-" + p.LastDay.Day.ToString();
                        
                        break;

                }
                Cell.HorizontalAlign = HorizontalAlign.Center;
                rw.Cells.Add(Cell);
            }
            result.Rows.Add(rw);
            return result;
        }

        /// <summary>
        /// Параметры визуализации
        /// </summary>
        private Grishko.UI.SideSheduleTable.SideSheduleParams _SideSheduleParams { get; set; }
        private  SideShedule  _Shedule { get; set; }

        public SideSheduleRow() { }
        public SideSheduleRow(  SideShedule SideShedule,
                                Grishko.UI.SideSheduleTable.SideSheduleParams SheduleParams,
                                string PageMethodOnCellClick                                 
                            ) 
        {
            _Shedule = SideShedule;
            _SideSheduleParams = SheduleParams;
            PageMethodFormat = PageMethodOnCellClick;            

        }

        public SideSheduleRow(Grishko.UI.SideSheduleTable.SideSheduleParams SheduleParams,
                                     string PageMethodOnCellClick
                                     )
        {
            _SideSheduleParams = SheduleParams;
            PageMethodFormat = PageMethodOnCellClick;
            

        }

        #region Tools
        /// <summary>
        /// устанавливает - ервый последний день в периода которому принадлежит дата StartDay
        /// </summary>
        /// <param name="StartDay"></param>
        /// <param name="FirstDay"></param>
        /// <param name="LastDay"></param>
        private void SetFirstLast_DayOfPeriod(DateTime StartDay, ref DateTime FirstDay, ref DateTime LastDay)
        {
            switch (_SideSheduleParams.Period)
            {
                case DateTimeTools.TimePeriod.Month:
                    FirstDay = DateTimeTools.GetFisrtDayInMonth(StartDay);
                    LastDay = DateTimeTools.GetLastDayInMonth(StartDay);
                    return;
                case DateTimeTools.TimePeriod.Week:
                    FirstDay = DateTimeTools.GetFisrtDayInWeek(StartDay);
                    LastDay = DateTimeTools.GetLastDayInWeek(StartDay);
                    return;

            }

            FirstDay = StartDay;
            LastDay = StartDay;
            return;
        }

        /// <summary>
        /// Набор периодов размерностью равной PeriodType
        /// </summary>
        public List<PeriodItem> PeriodList
        {
            get
            {
                // для месяцев - первый последний день месяца
                DateTime FirstDay = DateTime.Now;
                DateTime nullDay = DateTime.Now;
                DateTime LastDay = DateTime.Now;

                SetFirstLast_DayOfPeriod(_SideSheduleParams .dtStart, ref FirstDay, ref nullDay);
                SetFirstLast_DayOfPeriod(_SideSheduleParams.dtFinish, ref nullDay, ref LastDay);

                List<PeriodItem> l = new List<PeriodItem>();
                DateTime dt = FirstDay;
                for (int i = 0; i < Diff; i++)
                {
                    PeriodItem p = new PeriodItem();
                    p.FirstDay = dt;
                    p.LastDay = DateTimeTools.GetLastDayOfPeriod(dt, _SideSheduleParams.Period);
                    dt = p.LastDay.AddDays(1);
                    l.Add(p);
                }

                return l;
            }
        }
        
        private void MyRender(HtmlTextWriter writer)
        {
            // Формируем основную табличку 
            Table MainTable = new Table() {Width = new Unit ( "100%") ,CellPadding =2, CellSpacing =2};
            TableRow rw = new TableRow();
            // Идем по периодам              
            foreach (PeriodItem p in PeriodList)
            {
                //Формируем - ячепйку box
                TableCell Cell = new TableCell() { Width = BoxCellWidtch  };                
                //Cell.Text = FillTemplate_Day;
                // заполняем ячейку box - табличкой отражающей загрузку стороны
                FillSidePeriodShedule(Cell,p);
                rw.Cells.Add(Cell);


            }
            MainTable.Rows.Add(rw);
            MainTable.RenderControl(writer);
        }

        /// <summary>
        /// заполняем ячейку box - табличкой отражающей загрузку стороны
        /// </summary>
        /// <param name="Cell"></param>
        /// <param name="CellPeriod"></param>
        private void FillSidePeriodShedule(TableCell BoxCell,PeriodItem CellPeriod)
        {
            //Если существует очередь - то колчичество поверхностей=MaxSurfValue иначе реальному количеству поверхностей
            int j = (_Shedule.IHave_Q ? _Shedule.MaxSurfValue + 1 : _Shedule.Side.SurfaceCount);

            for (int i = 0; i < j; i++)
            {               
                List<StackItem> SurfaceStack = GetPeriodStack(CellPeriod, i);
                Table Row = GetInsideTable(SurfaceStack, (i > _Shedule.Side.SurfaceCount - 1));
                BoxCell.Controls.Add(Row);
                BoxCell.Controls.Add(RowDiv);

            }

        }

        private Table RowDiv { get 
        {
            Table div = new Table() { Width = new Unit("100%") };
            TableRow rw = new TableRow();
            TableCell cell = new TableCell() { Width = new Unit("100%"),  Height = new Unit("2px") };
            rw.Cells.Add(cell);
            div.Rows.Add(rw);
            return div;
        } }

        /// <summary>
        /// Сформировать конду на событие OnClick
        /// </summary>
        /// <param name="Cell"></param>
        /// <param name="i"></param>
        private void SetEvent(TableCell Cell, StackItem i)
        {
            PageMethodParam p = new PageMethodParam()
            {
                dtStart = i.FirstDay,
                dtEnd = i.LastDay,
                SideID = _Shedule.SideID

            };
            Cell.Attributes .Add ("OnClick",string.Format ( PageMethodFormat ,p.ImAsArray ));
        }
        
        /// <summary>
        /// Сформировать табличку отражающую стек переходов стороны
        /// </summary>
        /// <param name="Source"></param>
        /// <param name="IsQSurface"></param>
        /// <returns></returns>
        private Table GetInsideTable(List<StackItem> Source,bool IsQSurface)
        {
            
            int DayCount = 0;
            foreach (StackItem itm in Source)
            {
                DayCount = DayCount + itm.DayCount;
            }
            Table result = new Table() { Width = new Unit("100%"), CellPadding = 0, CellSpacing = 0 };
            TableRow rw = new TableRow();
            
            foreach (StackItem i in Source)
            {
                int iCellWidth = Convert.ToInt16(i.DayCount * 100 / DayCount);
                TableCell Cell = new TableCell() 
                { 
                   Width = new Unit(iCellWidth.ToString() + "%"), 
                   Height = (IsQSurface?Q_Height: Height)
                   
                };

                if (IsQSurface)                
                    Cell.CssClass = (i.ContractState == string.Empty ?string .Empty : SideSheduleRow.Convert_StateIdToCss (i.ContractState));                                    
                else                
                    Cell.CssClass = ( SideSheduleRow.Convert_StateIdToCss (i.ContractState));
                rw.Cells.Add(Cell);

                if(EditMode) // если разрешено редактировать расписание - привязываем событие
                    SetEvent(Cell, i);

                if (i.IsProcessedContractItemsCell)// если ячейка относится к текущему контракту
                    Cell.CssClass = Cell.CssClass + ' ' + Css_SelCell;    
                
            }
            result.Rows.Add(rw);
            return result;

        }
        
        /// <summary>
        /// Определить является ли отрезок принадлежащим текущему редактируемому контракту
        /// </summary>
        /// <param name="?"></param>
        /// <returns></returns>
        private bool IsProcessedContractItemsCell(SurfaceSheduleItem obj)
        {
            if (_SideSheduleParams.CurrentContract == null)
                return false;

            if (_SideSheduleParams.CurrentContract.ContractSideItemList.Count == 0)
                return false;

            if (null == obj)
                return false;

            _SideSheduleParams.CurrentContract.ContractSideItemList.Reset();
            foreach (TContractSide  i in _SideSheduleParams.CurrentContract.ContractSideItemList)
            {
                if (i.ContractID == obj.ContractId )
                    return true;
            }
            return false;
        
        }

        public List<StackItem> GetPeriodStack(PeriodItem Period, int SurfaceNumber)
        {
            DateTime FirstDay = Period.FirstDay;
            DateTime LastDay = Period.LastDay;
            SurfaceSheduleItem SurfaceSheduleItem = null;
            //идем по дням и строим стек переходов состояний 
            List<StackItem> stack = new List<StackItem>();
            StackItem CurrentState = new StackItem();
            CurrentState.FirstDay = FirstDay;
            CurrentState.LastDay = LastDay;
            DateTime dt = FirstDay;
            // описание периода 
            SurfaceSheduleItem = GetDayState(dt, SurfaceNumber);
            CurrentState.ContractState = (null == SurfaceSheduleItem ? string.Empty : SurfaceSheduleItem.ContractStatusTypeID);
            CurrentState.IsProcessedContractItemsCell = IsProcessedContractItemsCell(SurfaceSheduleItem);
            CurrentState.SurfaceNumber = SurfaceNumber;
            CurrentState.Hint = CurrentState.FirstDay.ToShortDateString() + "-" + CurrentState.LastDay.ToShortDateString();

            string LastSurfaceSheduleItemId = string.Empty;
            LastSurfaceSheduleItemId = (null == SurfaceSheduleItem ? string.Empty : SurfaceSheduleItem.ID);

            while (DateTimeTools.DayInPeriod(FirstDay, LastDay, dt))
            {
                SurfaceSheduleItem = GetDayState(dt, SurfaceNumber);

                string DayState = (null == SurfaceSheduleItem ? string.Empty : SurfaceSheduleItem.ContractStatusTypeID);
                if (CurrentState.ContractState == DayState) //Есть переход состояний
                //if (LastSurfaceSheduleItemId == (null == SurfaceSheduleItem ? string.Empty : SurfaceSheduleItem.ID))
                {
                    CurrentState.DayCount++;
                    CurrentState.LastDay = dt;
                }
                else // прошел переход состояний - состояние в стек
                {
                    //LastSurfaceSheduleItemId = (null == SurfaceSheduleItem ? string.Empty : SurfaceSheduleItem.ID);
                    stack.Add(CurrentState);
                    CurrentState = new StackItem() { ContractState = DayState, FirstDay = dt, LastDay = dt, DayCount = 1 };
                  //  CurrentState.IsProcessedContractItemsCell = IsProcessedContractItemsCell(SurfaceSheduleItem);
                    CurrentState.SurfaceNumber = SurfaceNumber;
                    CurrentState.Hint = CurrentState.FirstDay.ToShortDateString() + "-" + CurrentState.LastDay.ToShortDateString();

                }

                dt = dt.AddDays(1);
            }
            stack.Add(CurrentState);

            return stack;

        }

        /// 0 - не пересекаются
        /// 1- X входит в Y 
        /// 2- X левее Y
        /// 3- X правее Y
        /// 4- X==Y
        /// 5-X примыкает к Y 
        /// 6-Y примыкает к X
        /// Возвращает состояние поверхности для заданного дня
        private SurfaceSheduleItem GetDayState(DateTime Day, int SurfaceNumber)
        {
            if (_Shedule .Count == 0)
                return null;

            int CrossType = 0;
            SurfaceSheduleItem i = _Shedule.GetSurfaceSheduleItem(Day, Day, SurfaceNumber, ref CrossType);
            if (null != i)
                return i;
            else
                return null;

        }
        #endregion 



        /// <summary>
        /// Разница между началом и кончанием в периодах
        /// </summary>
        protected  int Diff
        {
            get
            {
                return DateTimeTools.GetDiff(_SideSheduleParams.dtStart, _SideSheduleParams.dtFinish, _SideSheduleParams.Period);
            }
        }
        
        protected override void OnDataBinding(EventArgs e)
        {
            //bEnableRander = true;
        }

        protected override void Render(HtmlTextWriter writer)
        {
            if (this.DesignMode)
            {
                writer.Write("Строка сетки загрузки");
                return;
            }

            //// прорисовываем только если есть источник
            if (null == _Shedule)
                return;

           // if (bEnableRander)
                MyRender(writer);

        }

    }

}
